'use strict';

import axios from 'axios';

import { Message } from 'element-ui';

/**
 * [汇总] 自定义 config 属性
 * allowDuplicateRequest {boolean} false 允许重复请求
 * restoreDataFormat {boolean} true 还原数据结构
 * successText {string} '' 成功提示文本
 * errorText {string} '' 失败提示文本
 * retry {number} 3 重试次数
 * retryDelay {number} 2 重试时间(单位s)
 */

const axiosInstance = axios.create({
  baseURL: '/',
  timeout: 3 * 1000,
});

// 根据不同环境更改不同 baseUrl
if (process.env.NODE_ENV === 'development') {
  axiosInstance.defaults.baseURL = '/api'; // 开发环境
} else if (process.env.NODE_ENV === 'production') {
  axiosInstance.defaults.baseURL = '/'; // 生产环境
}

// 请求队列
const requestQueue = new Map();

// 请求拦截器
axiosInstance.interceptors.request.use((config) => {

  // 请求之前做些什么
  // 例如：请求头添加 token
  let token = sessionStorage.getItem('token')
  token && (req.headers['Authorization'] = `Bearer ${token}`)
  // console.log('请求拦截', config);

  const { allowDuplicateRequest = false } = config;

  // 允许重复请求
  if (allowDuplicateRequest) {
    return config;
  }



  /**
   * 以下逻辑是处理 => 重复请求
   */

  // 生成请求key
  const requestKey = getRequestKey(config);

  // 创建请求控制器
  const controller = new AbortController();

  config.signal = controller.signal;

  // 判断请求队列是否存在相同请求
  if (requestQueue.has(requestKey)) {

    // 停止请求
    controller.abort();
  } else {
    // 把本次请求提交到队列
    requestQueue.set(requestKey, controller);
  }
  return config;
}, (error) => {
  // 请求错误
  console.log('请求错误', error);
  return Promise.reject(error);
});

// 响应拦截器
axiosInstance.interceptors.response.use((response) => {

  // 响应 status === 200
  resp.data.token && sessionStorage.setItem('token', resp.data.token)
  // console.log('响应拦截', response);
  const config = response.config;
  // 生成请求key
  const requestKey = getRequestKey(config);
  // 从请求队列移除掉
  requestQueue.delete(requestKey);
  // 还原数据结构, 成功提示文本, 失败提示文本
  const { restoreDataFormat = true, successText, errorText } = config;
  // 还原数据结构
  if (restoreDataFormat) {

    const { code, msg, data } = response.data;

    if (code === 200) {

      Message({
        message: successText || msg || '成功',
        type: 'success'
      });
    } else {

      Message({
        message: errorText || msg || '失败',
        type: 'error'
      });


      return Promise.reject(msg)
    }

    return data;
  }
  return response;
}, async (error) => {

  // 响应 status !== 200

  console.log('响应错误', error);

  const config = error.config;



  /**
   * 以下逻辑处理 => 请求重试
   * 重试时机: 超时
   */

  if (error.message.indexOf('timeout') !== -1) {
    // 生成请求key
    const requestKey = getRequestKey(config);
    // 从请求队列移除掉
    requestQueue.delete(requestKey);

    // 重试次数、重试时间、当前重试次数
    const { retry = 3, retryDelay = 2, retryCount = 0 } = config;

    // 延时处理
    const delay = new Promise((resolve) => {
      setTimeout(() => {
        resolve();
      }, retryDelay * 1000);
    });

    // 重试次数 && 当前重试次数未超过最大重试次数
    if (retry && (retryCount < retry)) {

      // 当前重试次数加一
      config.retryCount = retryCount + 1;

      console.log(`[请求异常] 尝试重新请求 => [${config.url}] => 第 ${config.retryCount} 次`);

      Message({
        message: `[请求异常] 尝试重新请求第 ${config.retryCount} 次`,
        type: 'warning'
      });

      // 重新发起请求
      return delay.then(function () {
        return baseRequest(config);
      });
    }

    Message({
      message: '接口异常, 请联系管理员',
      type: 'error'
    });
  }



  return Promise.reject(error);
});

// 基础请求
const baseRequest = (config) => {

  return axiosInstance(config);
}

const get = (url = '', params = {}, config = {}) => {

  const newConfig = Object.assign({}, {
    method: 'get',
    url,
    params,
  }, config);

  return baseRequest(newConfig)
}

const post = (url = '', data = {}, config = {}) => {

  const newConfig = Object.assign({}, {
    method: 'post',
    url,
    data,
  }, config);

  return baseRequest(newConfig)
}

const put = (url = '', data = {}, config = {}) => {

  const newConfig = Object.assign({}, {
    method: 'put',
    url,
    data,
  }, config);

  return baseRequest(newConfig)
}

const destroy = (url = '', data = {}, config = {}) => {

  const newConfig = Object.assign({}, {
    method: 'delete',
    url,
    data,
  }, config);

  return baseRequest(newConfig)
}

const upload = (url = '', data = {}, config = {}) => {

  const newConfig = Object.assign({}, {
    method: 'post',
    url,
    data,
    headers: {
      'Content-Type': 'multipart/form-data'
    },
    timeout: 60 * 1000,
  }, config);

  return baseRequest(newConfig)
}

// 字符串转hash
const strToHash = (str) => {
  let hash = 0;
  let chr;

  if (str.length === 0) {
    return hash
  }

  for (let i = 0; i < str.length; i++) {
    chr = str.charCodeAt(i);
    hash = (hash << 5) - hash + chr;
    hash |= 0;
  }

  return hash;
};

const getRequestKey = (config) => {
  let { url, method, data, params } = config;
  data = (typeof data === 'string') ? JSON.parse(data) : data;
  // // 把请求转为字符串
  const requestStr = `${method}_${url}_${params ? JSON.stringify(params) : JSON.stringify(data)}`;
  // // 设置请求key
  const requestKey = strToHash(requestStr);
  return requestKey;
}

const cancelAllRequest = () => {
  requestQueue.forEach((controller) => {
    // 停止请求
    controller.abort();
  });
  requestQueue.clear();
  return 'ok';
}
export default {
  axiosInstance,
  get,
  post,
  put,
  delete: destroy,
  upload,
  cancelAllRequest,
}
